use crate::protocol::{RedisCommand, RespValue};
use crate::storage::{Storage, StorageEngine, RedisValue};
use std::collections::HashMap;

/// HSET key field value
pub async fn hset(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() != 3 {
        return RespValue::error("ERR wrong number of arguments for 'hset' command");
    }

    let key = &command.args[0];
    let field = &command.args[1];
    let value = &command.args[2];

    let mut hash = match storage.get(key) {
        Some(RedisValue::Hash(existing_hash)) => existing_hash,
        Some(_) => return RespValue::error("WRONGTYPE Operation against a key holding the wrong kind of value"),
        None => HashMap::new(),
    };

    let is_new_field = !hash.contains_key(field);
    hash.insert(field.clone(), value.clone());

    storage.set(key.clone(), RedisValue::Hash(hash), None);
    RespValue::integer(if is_new_field { 1 } else { 0 })
}

/// HGET key field
pub async fn hget(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() != 2 {
        return RespValue::error("ERR wrong number of arguments for 'hget' command");
    }

    let key = &command.args[0];
    let field = &command.args[1];

    match storage.get(key) {
        Some(RedisValue::Hash(hash)) => {
            match hash.get(field) {
                Some(value) => RespValue::bulk_string(value.clone()),
                None => RespValue::null_bulk_string(),
            }
        }
        Some(_) => RespValue::error("WRONGTYPE Operation against a key holding the wrong kind of value"),
        None => RespValue::null_bulk_string(),
    }
}

/// HDEL key field [field ...]
pub async fn hdel(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() < 2 {
        return RespValue::error("ERR wrong number of arguments for 'hdel' command");
    }

    let key = &command.args[0];
    let fields = &command.args[1..];

    match storage.get(key) {
        Some(RedisValue::Hash(mut hash)) => {
            let mut deleted = 0;

            for field in fields {
                if hash.remove(field).is_some() {
                    deleted += 1;
                }
            }

            if hash.is_empty() {
                storage.del(key);
            } else {
                storage.set(key.clone(), RedisValue::Hash(hash), None);
            }

            RespValue::integer(deleted)
        }
        Some(_) => RespValue::error("WRONGTYPE Operation against a key holding the wrong kind of value"),
        None => RespValue::integer(0),
    }
}

/// HGETALL key
pub async fn hgetall(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() != 1 {
        return RespValue::error("ERR wrong number of arguments for 'hgetall' command");
    }

    let key = &command.args[0];

    match storage.get(key) {
        Some(RedisValue::Hash(hash)) => {
            let mut result = Vec::new();

            for (field, value) in hash.iter() {
                result.push(RespValue::bulk_string(field.clone()));
                result.push(RespValue::bulk_string(value.clone()));
            }

            RespValue::array(result)
        }
        Some(_) => RespValue::error("WRONGTYPE Operation against a key holding the wrong kind of value"),
        None => RespValue::array(vec![]),
    }
}

/// HEXISTS key field
pub async fn hexists(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() != 2 {
        return RespValue::error("ERR wrong number of arguments for 'hexists' command");
    }

    let key = &command.args[0];
    let field = &command.args[1];

    match storage.get(key) {
        Some(RedisValue::Hash(hash)) => {
            RespValue::integer(if hash.contains_key(field) { 1 } else { 0 })
        }
        Some(_) => RespValue::error("WRONGTYPE Operation against a key holding the wrong kind of value"),
        None => RespValue::integer(0),
    }
}